package cn.jolyoulu.common.web.utils;


import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * OkHttpProxyFactory 代理工厂
 * @Author LuZhouJin
 * @Date 2023/8/27
 */
@Slf4j
@Component
public class OkHttpProxyFactory {

    //请求数据类型
    public final static String CONTENT_TYPE = "application/json";
    //请求前缀
    private String baseUrl = "";

    //OkHttpClient连接池
    private final static OkHttpClient okHttpClientPool = new OkHttpClient.Builder()
            //设置连接池大小
            .connectionPool(new ConnectionPool(100,5,TimeUnit.SECONDS))
            //这是建立到服务器的连接的最大允许时间
            .connectTimeout(3, TimeUnit.SECONDS)
            //这是读取从服务器返回的数据的最大允许时间
            .readTimeout(3, TimeUnit.SECONDS)
            //这是发送请求数据到服务器的最大允许时间
            .writeTimeout(3, TimeUnit.SECONDS)
            .build();

    /**
     * 本地缓存
     */
    private final static Map<Class<?>, Object> cache = new ConcurrentHashMap<>();

    /**
     * 获取一个代理对象
     *
     * @param clientInterface
     * @param <T>
     * @return
     */
    public <T> T getOrCreateClient(Class<T> clientInterface) {
        if (cache.containsKey(clientInterface)) {
            return (T) cache.get(clientInterface);
        } else {
            T client = createClient(clientInterface);
            cache.putIfAbsent(clientInterface, client);
            return client;
        }
    }

    /**
     * 获取一个代理对象
     *
     * @param clientInterface
     * @param <T>
     * @return
     */
    private <T> T createClient(Class<T> clientInterface) {
        if (!clientInterface.isInterface()) {
            throw new IllegalArgumentException("");
        }
        @SuppressWarnings("unchecked")
        T res = (T) Proxy.newProxyInstance(clientInterface.getClassLoader(),
                new Class[]{clientInterface},
                ((proxy, method, args) -> {
                    Response response = null;
                    try {
                        long startTime = System.nanoTime();
                        //get  请求
                        GetMapping getMapping = method.getAnnotation(GetMapping.class);
                        if (Objects.nonNull(getMapping)) {
                            String url = genUrl(getMapping.value()[0], method, args);
                            response = doGet(url);
                        }
                        //post 请求
                        PostMapping postMapping = method.getAnnotation(PostMapping.class);
                        if (Objects.nonNull(postMapping)) {
                            String url = genUrl(postMapping.value()[0], method, args);
                            Annotation[][] annotations = method.getParameterAnnotations();
                            if (annotations.length > 0) {
                                Annotation annotation = annotations[0][0];
                                if (annotation instanceof org.springframework.web.bind.annotation.RequestBody) {
                                    response = doPost(url, RequestBody.create(MediaType.parse(OkHttpProxyFactory.CONTENT_TYPE),
                                            JSONObject.toJSONString(args[0])));
                                }
                            } else {
                                response = doPost(url);
                            }
                        }
                        log.info("OkHttpProxyFactory response ==> code:{} time:{}ms", response.code(),(System.nanoTime() - startTime) / 1_000_000);
                        if (response.isSuccessful()) {
                            //获取返回对象class
                            return JSONObject.parseObject(JSONObject.toJSONString(response.body().string()), method.getGenericReturnType());
                        }
                    }catch (Exception e){
                        log.error("OkHttpProxyFactory 请求发送失败",e);
                    }finally {
                        if (Objects.nonNull(response)){
                            response.close();
                        }
                    }
                    return proxy;
                }));
        return res;
    }

    /**
     * 根据方法参数构建Url
     *
     * @return
     */
    private String genUrl(String url, Method method, Object[] args) {
        StringBuilder sb = new StringBuilder().append("?");
        Annotation[][] annotations = method.getParameterAnnotations();
        //路径拼接
        for (int i = 0; i < annotations.length; i++) {
            Annotation argAnn = annotations[i][0];
            if (argAnn instanceof PathVariable) {
                String pathVar = ((PathVariable) argAnn).value();
                url = url.replace("{" + pathVar + "}", String.valueOf(args[i]));
            }
            if (argAnn instanceof RequestParam) {
                String reqParam = ((RequestParam) argAnn).value();
                if (sb.length() == 1) {
                    sb.append(reqParam).append("=").append(args[i]);
                } else {
                    sb.append("&").append(reqParam).append("=").append(args[i]);
                }
            }
        }
        if (sb.length() > 1) {
            url = url + sb;
        }
        return url;
    }

    private Request.Builder initRequest(String url){
        if (StringUtils.isBlank(baseUrl)){
            throw new RuntimeException("baseUrl 不能为空");
        }
        log.info("OkHttpProxyFactory request ==> {}", baseUrl + url);
        return new Request.Builder()
                .url(baseUrl + url)
                .addHeader("Content-Type", OkHttpProxyFactory.CONTENT_TYPE);
    }

    /**
     * 发起get请求
     */
    private Response doGet(String url) throws IOException {
        return okHttpClientPool
                .newCall(initRequest(url).get().build())
                .execute();
    }

    /**
     * 发起post请求
     */
    private Response doPost(String url) throws IOException {
        RequestBody body = RequestBody.create(MediaType.parse(OkHttpProxyFactory.CONTENT_TYPE), JSONObject.toJSONString(""));
        return okHttpClientPool
                .newCall(initRequest(url).post(body).build())
                .execute();
    }

    /**
     * 发起post请求
     */
    private Response doPost(String url, RequestBody body) throws IOException {
        return okHttpClientPool
                .newCall(initRequest(url).post(body).build())
                .execute();
    }
}
